//模块加载器
import fs from 'fs'
import {createRequire} from "module";

import tsc from "../utils/TscUtils"
import sugar from './sugar'
import errorCatch from '../entrance/event/errorCatch';
import color from '../utils/Color';
import Ioc from './Ioc';
//cjs导入器
const Require:any = createRequire(__filename)

export = Inc;

class Inc{
  // 创建加载器
  public static createLoader(module:object = {}){
    Object.keys(module).forEach(e=>{
        if(typeof module[e] === "string"){
            // 加载一次就会被缓存到Ioc容器中
            loader(sugar.path(module[e]))
        }
    })
    return loader
  }

  //导入mjs模块的方法
  public static async mjs(module:any){
    let mj = await import("file://"+module.id);
    module.exports = mj.default ? mj.default : mj;
    module.loaded = true;
  }
    
  //导入cjs的方法
  public static cjs(module:any){
    let data = fs.readFileSync(module.id,"utf8");
    //这是一个模块
    if(/^[ ]*module\.exports/m.test(data) || /^[ ]*exports\./m.test(data)){
        module.exports = Inc.CommonJS(module.id);
    }else if(/^[ ]*export default/m.test(data) || /^[ ]*export /m.test(data)){
        module.exports = Inc.ECMAScript(module.id);
    }else{
        module.exports = Require(module.id);
    }
    module.loaded = true;
  }

  //加载这个协议封装的模块
  public static CommonJS(id:string){
    try{
        let methods = Require(id);
        return methods?.default || methods;
    }catch(err){
        errorCatch(err,'loader.136')
    }
    
  }
  public static async ECMAScript(id:string){
    try{
        let methods = await import(id);
        return methods?.default || methods;
    }catch(err){
        console.log(id)
        errorCatch(err,'loader.145')
    }
  }

  //导入ts方法
  public static ts(module:any){
    // 编译好后的js文件位置
    let outdir = module.id.replace(/[\/\\][^/\\]*$/g,"")+"\\.ts";
    let data = tsc(module.id,outdir);
    if(data.includes("err")){console.log(data);throw Error(data)}
    // 引入这个js
    outdir+(module.id.match(/[\/\\][^/\\]*?\./g)?.[0])+"js";
    // 判断是否为全局模块,是的话获取该模块的路径并且把路径解析出来
    module.exports = Require(module.id.match(/\.[^./\\]*/,"")+".js")
    module.loaded = true;
  }

  public static json(module:any){
    module.exports = Require(module.id)
    module.loaded = true
  }

}


const extensions = {
  ".js": Inc.cjs,
  ".cjs":Inc.cjs,
  ".mjs":Inc.mjs,
  ".json":Inc.json,
  ".ts":Inc.ts
} 

function Module(id:string){
  return {
      id,
      exports:{},
      loaded:false
  }
}

//核心文件，保存所有控制器的内容，并执行缓存：规则，控制器调用别的模块必须使用绝对路径
//开发中禁用缓存，使路由热启动 //启动加载器
function loader(id:string,bool:boolean=false){
  if(id===undefined) throw new Error("id is not null")

  //查一下缓存
  if(bool===false && Ioc.getClass(id))return Ioc.getClass(id)

  let path = id;
  //判断是否为全局模块,是的话获取该模块的路径并且把路径解析出来
  path = sugar.path(id,{superNumber:3});

  //这有可能是系统模块或第三方模块
  if(!/^[./\\]|^[A-Z]\:/g.test(path)){return Require(path)?.default || Require(path)}

  function isthereAre(filepath:string="",i=0,suffixarr = Object.keys(extensions)){
      if(i>=suffixarr.length){
          console.log(color("红色",path),"模块不存在")
          throw new Error("找不到模块")
      }    
      if(fs.existsSync(filepath) && fs.statSync(filepath)?.isFile()){
          // 添加到缓存中
          let module = Module(filepath);
          if(extensions[filepath.match(/\.[^.\\/]*$/)?.[0]]){
              extensions[filepath.match(/\.[^.\\/]*$/)?.[0]](module)
              Ioc.add(id,filepath,module.exports)
          }else{
              // 使用默认cjs加载
              extensions[".cjs"](module);
              Ioc.add(id,filepath,module.exports);
          }
      }else{
          isthereAre(path+suffixarr[i++],i,suffixarr)
      }
  }
  // 到了这里说明模块存在
  isthereAre(path);
  // 添加缓存
  return Ioc.getClass(id);
}